package org.univ.fblikes;

import org.apache.commons.lang3.StringUtils;
import org.springframework.social.facebook.api.*;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.logging.Logger;

/**
 * User: dzirtbry
 * Date: 5/20/13
 * Time: 10:49 PM
 */
@Component
public class PostAnalyzer {
    private static final Logger LOGGER = Logger.getLogger(PostAnalyzer.class.getName());

    // Bad practice, to keep string of spec-chars, but regex is much more complex to use with UTF, and every thing
    // else is even worse
    private static final String IGNORED_CHARACTERS = ".,!@#$%^&*()_+=-`~\'\"<>?`[]{}/\\;:| ";

    private static final int LIKE_SCORE = 1;
    private static final int COMMENT_SCORE = 1;
    private static final int SHARE_SCORE = 3;

    public <T extends Post> Map<String, Integer> analyzeMessages(List<T> posts) {
        Map<String, Integer> res = new HashMap<String, Integer>();
        for (Post post : posts) {
            // Get message from post. Messages are only available for status posts,
            // for other types of posts they are null, and something else is used (like caption, title, story, etc).
            String message = post.getMessage();

            int score = getScore(post);

            if (StringUtils.isEmpty(message)) {
                // Skip empty or null messages
                continue;
            }
            String[] words = StringUtils.split(message);

            for (String w : words) {
                String word = trimSpecialCharacters(w);
                if (word == null || word.length() < 3) {
                    continue;
                }
                if (res.get(word) == null) {
                    res.put(word, score);
                } else {
                    res.put(word, score + res.get(word));
                }
            }
        }
        return res;
    }

    private int getScore(Post post) {
        List<Reference> postLikes = post.getLikes();
        List<Comment> postComments = null;
        try {
            postComments = post.getComments();
        } catch (Exception ignored) {
            // getComments throws NPE if there is no comments...  XD

        }
        int likes = postLikes == null ? 0 : postLikes.size();
        int shares = post.getSharesCount();
        int comments = postComments == null ? 0 : postComments.size();
        return likes * LIKE_SCORE + comments * COMMENT_SCORE + shares * SHARE_SCORE;
    }

    public List<Word> convertToWords(Map<String, Integer> results) {
        List<Word> res = new ArrayList<>(results.size());
        for (String key : results.keySet()) {
            res.add(new Word(key, results.get(key)));
        }
        Collections.sort(res);
        return res;
    }

    public List<Word> getWordsStatistics(Facebook facebook, String id, int days) {
        Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
        calendar.add(Calendar.DATE, -days);
        Date startingDay = calendar.getTime();

        // TODO : improve to use since/until dates... Was not working from scratch with simple dates, wondering why,
        // perhaps issue in spring-social library
//        PagingParameters params = new PagingParameters(null, null, startingDay.getTime(), null);
//        PagedList<StatusPost> posts = facebook.feedOperations().getStatuses(id, params);

        // Fetching posts until we get post with date older than expected, or we exceed first post
        boolean more = true;
        List<Post> toAnalyze = new ArrayList<>();
        PagedList<StatusPost> posts = facebook.feedOperations().getStatuses(id);
        while (more) {
            if (posts.size() == 0) {
                // No more post, stop
                break;
            }
            for (Post post : posts) {
                // post.getCreatedTime() is always returned as null, so using getUpdatedTime instead.
                if (post.getUpdatedTime().after(startingDay)) {
                    toAnalyze.add(post);
                } else {
                    // All other posts are older then expected, stop
                    more = false;
                    break;
                }
            }
            if (more) {
                // If we need more posts
                posts = facebook.feedOperations().getStatuses(id, posts.getNextPage());
            }
        }

        LOGGER.info("Fetched " + posts.size() + " posts");


        Map<String, Integer> stringIntegerMap = analyzeMessages(toAnalyze);

        return convertToWords(stringIntegerMap);
    }

    /**
     * Trim word, removing leading an tailing special characters, defined in IGNORED_CHARACTERS.
     * <p/>
     * If resulting string is empty - return null
     *
     * @param x String to trim
     * @return trimmed string
     */
    private String trimSpecialCharacters(String x) {
        x = StringUtils.trimToEmpty(x);
        StringBuilder sb = new StringBuilder(x);

        while (sb.length() != 0 && StringUtils.containsAny(IGNORED_CHARACTERS, sb.charAt(0))) {
            sb.deleteCharAt(0);
        }

        while (sb.length() != 0 && StringUtils.containsAny(IGNORED_CHARACTERS, sb.charAt(sb.length() - 1))) {
            sb.deleteCharAt(sb.length() - 1);
        }

        String res = sb.toString();
        res = StringUtils.trimToNull(res);
        return res;
    }

}
